#include "WangTiles.h"
#include <il/il.h>


WangTile::WangTile()
{
	points = new Vec3[MAX_POINTS];
	toneScale = 200000;

	densTex = NULL;
	numTiles = 0;
	for (int i = 0; i < numTiles; i++)
	{
		for (int j = 0; j < numSubdivs; j++)
			if (tiles[i].subdivs[j] != NULL) delete[] tiles[i].subdivs[j];
		if (tiles[i].subdivs != NULL) delete[] tiles[i].subdivs;
		if (tiles[i].points != NULL) delete[] tiles[i].points;
		if (tiles[i].subPoints != NULL) delete[] tiles[i].subPoints;
	}
	tiles = NULL;
}

WangTile::~WangTile()
{
	if (points != NULL)
		delete[] points;
}

// Sample the density texture. By default we use linear filtering here.
float WangTile::sampleDensMap(float x, float y)
{
#define SAMPLE_LINEAR
#ifdef SAMPLE_LINEAR
	float tx = x*densTexSize;
	float ty = y*densTexSize;
	int ix = clampi(floori(tx), 0, densTexSize-2);
	int iy = clampi(floori(ty), 0, densTexSize-2);
	tx -= ix;
	ty -= iy;
	float sample = (densTex[iy*densTexSize+ix]*(1-tx)*(1-ty) + 
		densTex[iy*densTexSize+ix+1]*tx*(1-ty) + 
		densTex[(iy+1)*densTexSize+ix]*(1-tx)*ty + 
		densTex[(iy+1)*densTexSize+ix+1]*tx*ty);
	return sample;
#else
	int ix = clampi(x*densTexSize, 0, densTexSize-1);
	int iy = clampi(y*densTexSize, 0, densTexSize-1);
	return densTex[iy*densTexSize+ix];
#endif
}

void WangTile::recurseTile(Tile & t, float x, float y, int level)
{
	float tileSize = 1.f/powf(float(numSubtiles), float(level));
	if ((x+tileSize < clipMinX) || (x > clipMaxX) || (y+tileSize < clipMinY) || (y > clipMaxY))
		return;

	int numTests = mini(t.numSubPoints, int(powf(vpos.z, -2.f)/powf(float(numSubtiles), 2.f*level)*toneScale-t.numPoints));
	float factor = 1.f/powf(vpos.z, -2.f)*powf(float(numSubtiles), 2.f*level)/toneScale;

	for (int i = 0; i < numTests; i++)
	{
		float px = x+t.subPoints[i].x*tileSize, py = y+t.subPoints[i].y*tileSize;

		// skip point if it lies outside of the clipping window
		if ((px < clipMinX) || (px > clipMaxX) || (py < clipMinY) || (py > clipMaxY))
			continue;

		// reject point based on its rank
		//if (sampleDensMap(px, py) < (i+t.numPoints)*factor)
		//	continue;

		// "draw" point
		points[numPoints].x = px;
		points[numPoints].y = py;
		points[numPoints].z = (i+t.numPoints)*factor;
		numPoints++;
	}

	// recursion
	//if (powf(vpos.z, -2.f)/powf(float(numSubtiles), 2.f*level)*toneScale-t.numPoints > t.numSubPoints)
	if (level < 3)
	{
		for (int ty = 0; ty < numSubtiles; ty++)
			for (int tx = 0; tx < numSubtiles; tx++)
			{
				recurseTile(tiles[t.subdivs[0][ty*numSubtiles+tx]], x+tx*tileSize/numSubtiles, y+ty*tileSize/numSubtiles, level+1);
			}
	}
}

void WangTile::paintPoints()
{
	vpos = Vec3(0, 0, 1);
	clipMinX = vpos.x;
	clipMaxX = vpos.x+vpos.z;
	clipMinY = vpos.y;
	clipMaxY = vpos.y+vpos.z;
	numPoints = 0;

	float startTime = getTime();

	int numTests = mini((int)tiles[0].numPoints, int(powf(vpos.z, -2.f)*toneScale));
	float factor = 1.f/powf(vpos.z, -2)/toneScale;
	for (int i = 0; i < numTests; i++)
	{
		float px = tiles[0].points[i].x, py = tiles[0].points[i].y; 

		// skip point if it lies outside of the clipping window
		if ((px < clipMinX) || (px > clipMaxX) || (py < clipMinY) || (py > clipMaxY))
			continue;

		// reject point based on its rank
		//if (sampleDensMap(px, py) < i*factor)
		//	continue;

		// "draw" point
		points[numPoints].x = px;
		points[numPoints].y = py;
		numPoints++;
	}

	// recursion
	recurseTile(tiles[0], 0, 0, 0);

	float endTime = getTime();

	printf("%d points in %.2f ms = %.0f points/s\n", numPoints, (endTime-startTime)*1000, numPoints/(endTime-startTime));
}

void WangTile::savePoints(const char * fileName)
{
	FILE * fout = fopen(fileName, "wb");

	fwritei(fout, numPoints);
	for (int i = 0; i < numPoints; i++)
	{
		fwritef(fout, points[i].x);
		fwritef(fout, points[i].y);
	}

	fclose(fout);
}

void WangTile::loadTileSet(const char * fileName)
{
	//free memory
	if (densTex != NULL) delete[] densTex;
	for (int i = 0; i < numTiles; i++)
	{
		for (int j = 0; j < numSubdivs; j++)
			if (tiles[i].subdivs[j] != NULL) delete[] tiles[i].subdivs[j];
		if (tiles[i].subdivs != NULL) delete[] tiles[i].subdivs;
		if (tiles[i].points != NULL) delete[] tiles[i].points;
		if (tiles[i].subPoints != NULL) delete[] tiles[i].subPoints;
	}
	if (tiles != NULL) delete[] tiles;
	///////////////////////////////////////////////

	FILE * fin = fopen(fileName, "rb");

	numTiles = freadi(fin);
	numSubtiles = freadi(fin);
	numSubdivs = freadi(fin);

	tiles = new Tile[numTiles];

	for (int i = 0; i < numTiles; i++)
	{
		tiles[i].n = freadi(fin);
		tiles[i].e = freadi(fin);
		tiles[i].s = freadi(fin);
		tiles[i].w = freadi(fin);

		tiles[i].subdivs = new int * [numSubdivs];
		for (int j = 0; j < numSubdivs; j++)
		{
			int * subdiv = new int[sqri(numSubtiles)];
			for (int k = 0; k < sqri(numSubtiles); k++)
				subdiv[k] = freadi(fin);
			tiles[i].subdivs[j] = subdiv;
		}

		tiles[i].numPoints = freadi(fin);
		tiles[i].points = new Vec2[tiles[i].numPoints];
		for (int j = 0; j < tiles[i].numPoints; j++)
		{
			tiles[i].points[j].x = freadf(fin);
			tiles[i].points[j].y = freadf(fin);
			freadi(fin);freadi(fin);freadi(fin);freadi(fin);
		}

		tiles[i].numSubPoints = freadi(fin);
		tiles[i].subPoints = new Vec2[tiles[i].numSubPoints];
		for (int j = 0; j < tiles[i].numSubPoints; j++)
		{
			tiles[i].subPoints[j].x = freadf(fin);
			tiles[i].subPoints[j].y = freadf(fin);
			freadi(fin);freadi(fin);freadi(fin);freadi(fin);
		}
	}

	fclose(fin);
}